//
// Created by tcj on 2021/12/9.
//

#ifndef SERVER_FRAMEWORK_CONFIG_H
#define SERVER_FRAMEWORK_CONFIG_H
#include "memory"
#include "sstream"
#include "boost/lexical_cast.hpp"
#include "string"
#include "util.h"
#include "thread"
#include "map"
#include "yaml-cpp/yaml.h"
#include "set"
#include "unordered_map"
#include "unordered_set"
#include "list"
#include "functional"

#include "thread.h"
#include "log.h"

namespace sylar{

//    定义一个基类，主要放置一些共用的属性
//添加两个字段：m_name,m_description
    class ConfigVarBase{
    public:
        typedef std::shared_ptr<ConfigVarBase> ptr;
        ConfigVarBase(const std::string& name,const std::string& description="")
            :m_name(name),
            m_description(description){
            // tolower:大小写的转换，
            // transform：遍历对begin()到end()进行op操作--即进行大小写转换
            std::transform(m_name.begin(),m_name.end(),m_name.begin(),::tolower);
        }

        virtual ~ConfigVarBase(){}
        const std::string getName() const {return m_name;}
        const std::string get_description() const {return m_description;}

        virtual std::string toString() =0;
        virtual bool fromString(const std::string val)=0;
        virtual std::string getTypeName() const=0;
    protected:
        std::string m_name;
        std::string m_description;
    };

//   F form_type T to_type
    // f-->t
    template<class F,class T>
    class LexicalCast{
    public:
        T operator()(const F& v){
            return boost::lexical_cast<T>(v);
        }
    };

//   对上述方法的偏特化
    template<class T>
    class LexicalCast<std::string,std::vector<T>>{
    public:
        std::vector<T> operator() (const std::string& v){
            YAML::Node node = YAML::Load(v);
            typename std::vector<T> vec;
            std::stringstream ss;
            for(size_t i=0;i<node.size();i++){
                ss.str("");
                ss<<node[i];
                vec.push_back(LexicalCast<std::string,T>() (ss.str()));
            }
            return vec;
        }
    };


    template<class T>
    class LexicalCast<std::vector<T>,std::string>{
    public:
        std::string operator() (const std::vector<T>& v){
            YAML::Node node;
            for(auto& i:v){
                node.push_back(YAML::Load(LexicalCast<T,std::string>()(i)));
            }
            std::stringstream ss;
            ss<<node;
            return ss.str();
        }
    };


    template<class T>
    class LexicalCast<std::string,std::list<T>>{
    public:
        std::list<T> operator() (const std::string& v){
            YAML::Node node = YAML::Load(v);
            typename std::list<T> vec;
            std::stringstream ss;
            for(size_t i=0;i<node.size();i++){
                ss.str("");
                ss<<node[i];
                vec.push_back(LexicalCast<std::string,T>() (ss.str()));
            }
            return vec;
        }
    };

    template<class T>
    class LexicalCast<std::list<T>,std::string>{
    public:
        std::string operator() (const std::list<T>& v){
            YAML::Node node;
            for(auto& i:v){
                node.push_back(YAML::Load(LexicalCast<T,std::string>()(i)));
            }
            std::stringstream ss;
            ss<<node;
            return ss.str();
        }
    };


    template<class T>
    class LexicalCast<std::string,std::set<T>>{
    public:
        std::set<T> operator() (const std::string& v){
            YAML::Node node = YAML::Load(v);
            typename std::set<T> vec;
            std::stringstream ss;
            for(size_t i=0;i<node.size();i++){
                ss.str("");
                ss<<node[i];
                vec.insert(LexicalCast<std::string,T>() (ss.str()));
            }
            return vec;
        }
    };

    template<class T>
    class LexicalCast<std::set<T>,std::string>{
    public:
        std::string operator() (const std::set<T>& v){
            YAML::Node node;
            for(auto& i:v){
                node.push_back(YAML::Load(LexicalCast<T,std::string>()(i)));
            }
            std::stringstream ss;
            ss<<node;
            return ss.str();
        }
    };

    template<class T>
    class LexicalCast<std::string,std::unordered_set<T>>{
    public:
        std::unordered_set<T> operator() (const std::string& v){
            YAML::Node node = YAML::Load(v);
            typename std::unordered_set<T> vec;
            std::stringstream ss;
            for(size_t i=0;i<node.size();i++){
                ss.str("");
                ss<<node[i];
                vec.insert(LexicalCast<std::string,T>() (ss.str()));
            }
            return vec;
        }
    };

    template<class T>
    class LexicalCast<std::unordered_set<T>,std::string>{
    public:
        std::string operator() (const std::unordered_set<T>& v){
            YAML::Node node;
            for(auto& i:v){
                node.push_back(YAML::Load(LexicalCast<T,std::string>()(i)));
            }
            std::stringstream ss;
            ss<<node;
            return ss.str();
        }
    };

    template<class T>
    class LexicalCast<std::string,std::map<std::string ,T> >{
    public:
        std::map<std::string,T> operator() (const std::string& v){
            YAML::Node node = YAML::Load(v);
            typename std::map<std::string,T> vec;
            std::stringstream ss;
            for(auto it=node.begin();it!=node.end();it++){
                ss.str("");
                ss<<it->second;
                vec.insert(std::make_pair(it->first.Scalar(),LexicalCast<std::string,T>()(ss.str())));
            }
            return vec;
        }
    };
    template<class T>
    class LexicalCast<std::map<std::string,T>,std::string>{
    public:
        std::string operator() (const std::map<std::string,T>& v){
            YAML::Node node;
            for(auto& i : v){
                node[i.first] = (YAML::Load(LexicalCast<T,std::string>()(i.second)));
            }
            std::stringstream ss;
            ss<<node;
            return ss.str();
        }
    };


    template<class T>
    class LexicalCast<std::string,std::unordered_map<std::string ,T> >{
    public:
        std::unordered_map<std::string,T> operator() (const std::string& v){
            YAML::Node node = YAML::Load(v);
            typename std::unordered_map<std::string,T> vec;
            std::stringstream ss;
            for(auto it=node.begin();it!=node.end();it++){
                ss.str("");
                ss<<it->second;
                vec.insert(std::make_pair(it->first.Scalar(),LexicalCast<std::string,T>()(ss.str())));
            }
            return vec;
        }
    };
    template<class T>
    class LexicalCast<std::unordered_map<std::string,T>,std::string>{
    public:
        std::string operator() (const std::unordered_map<std::string,T>& v){
            YAML::Node node;
            for(auto& i : v){
                node[i.first] = (YAML::Load(LexicalCast<T,std::string>()(i.second)));
            }
            std::stringstream ss;
            ss<<node;
            return ss.str();
        }
    };
//    配置项类
//FormStr T operator() (const std::string&)
//ToStr std::string operator() (const T&)
    template<typename T,class FromStr=LexicalCast<std::string,T>,
            class ToStr=LexicalCast<T,std::string> >
    class ConfigVar : public ConfigVarBase{
    public:
        typedef RWMutex RWMutexType;
        typedef std::shared_ptr<ConfigVar> ptr;
        typedef std::function<void(const T& old_value,const T& new_value)> on_change_cb;

        ConfigVar(const std::string& name,const T& default_value,const std::string& description = "")
            : ConfigVarBase(name,description),m_val(default_value){
        }

        std::string toString() override{
            try{
//boost::lexical_cast:将T参数进行转换成string类型 如果转换失败将会抛出异常bad_lexical_cast
//                return boost::lexical_cast<std::string,T>(m_val);
                RWMutexType::ReadLock lock(m_mutex);
                return ToStr()(m_val);
            }catch (std::exception& e){
                SYLAR_LOG_ERROR(SYLAR_LOG_ROOT() ) << "Config::toString exception"<<e.what()
                                <<"convert: "<<typeid(m_val).name()<<" to string";
            }
            return "";
        }

        bool fromString(const std::string val) override{
            try{
//              将string类型转换成T类型
//              m_val = boost::lexical_cast<T>(val);
                setValue(FromStr()(val));
            }catch (std::exception& e){
                SYLAR_LOG_ERROR(SYLAR_LOG_ROOT()) << "Config::toString exception"
                <<e.what()<<"convert: string to "<<typeid(m_val).name()
                <<" - "<<val;
            }
            return false;
        }
        //得到配置信息
        const T getValue() {
            RWMutexType::ReadLock lock(m_mutex);
            return m_val;
        }
        //设置配置信息
        void setValue(const T& v){
            {
                RWMutexType::ReadLock lock(m_mutex);
                if (v == m_val) {
                    return;
                }
                for (auto &i: m_cbs) {
                    i.second(m_val, v);
                }
            }
            RWMutexType::WriteLock lock(m_mutex);
            m_val = v;
        }
//        typeid(T).name():输出运行时的类别名称
        std::string getTypeName() const override {return typeid(T).name();}


        uint64_t addListener(on_change_cb cb){
            static uint64_t s_fun_id=0;
            RWMutexType::WriteLock lock(m_mutex);
            ++s_fun_id;
            m_cbs[s_fun_id] = cb;
            return s_fun_id;
        }

        void delListener(uint64_t key){
            RWMutexType::WriteLock lock(m_mutex);
            m_cbs.erase(key);
        }

        on_change_cb getListener(uint64_t key){
            RWMutexType::ReadLock lock(m_mutex);
            auto it = m_cbs.find(key);
            return it==m_cbs.end()? nullptr:it->second;
        }
        void clearListener(){
            RWMutexType::WriteLock lock(m_mutex);
            m_cbs.clear();
        }

    private:
// 变更回调函数组，uint64_t key，要求唯一，一般可以用hash
// 主要是因为functional没有比较函数，只能通过包装一个map数组进行比较
        RWMutexType m_mutex;
        std::map<uint64_t,on_change_cb> m_cbs;
        T m_val;
    };


//    管理配置
    class Config{
    public:
        typedef std::map<std::string,ConfigVarBase::ptr> ConfigVarMap;
//先是根据名称查找,找到就返回,没找到就先利用find_first_not_of判断一下名字是否有非法字符(只能包含字母数字_)
//若不含,再用传入的默认值和描述字符串创建一个新的配置加入map中,并返回
        typedef RWMutex RWMutexType;

        template<typename T>
        static typename ConfigVar<T>::ptr Lookup(const std::string name,const T& default_value,
                                                  const std::string& description=""){
            //因为类型不同的话，需要在s_data中进行重新的标识，之前只是简单的判断一下值是否相同
            //主要是针对当key相同的时候，但是类型不同的时候，需要对其进行输出，并标识一下，
            //但是key和类型都相同的时候，直接返回
            RWMutexType::WriteLock lock(GetMutex());
            auto it = GetDatas().find(name);
            if(it != GetDatas().end()){
                auto tmp = std::dynamic_pointer_cast<ConfigVar<T> >(it->second);
                if(tmp){
                    SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << "Lookup name=" <<name <<"exits";
                    return tmp;
                }else{
                    SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << "Lookup name=" <<name <<" exits but type not "
                    <<typeid(T).name()<<" real_type=" << it->second->getTypeName()
                    <<" "<<it->second->toString();
                }
            }
//            auto temp = Lookup<T>(name);
//            if(temp){
//                SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << "Lookup name="<< name<<"exits";
//                return temp;
//            }
//       find_first_not_of():正向查找在原字符串中第一个与指定字符串中任一字符都不匹配的字符
//          若是查找失败,返回npos
            if(name.find_first_not_of("abcdefghijklmnopqrstuvwxyz._0123456789")
                                    != std::string::npos){
                SYLAR_LOG_ERROR(SYLAR_LOG_ROOT())<<"lookup name invaild "<<name;
                throw std::invalid_argument(name);
            }
            //如果没有当前string，，则将传递过来的信息进行保存
            typename sylar::ConfigVar<T>::ptr v(new ConfigVar<T>(name,default_value,description));
            GetDatas()[name] = v;
            return v;
        }
//        lookup是根据名字在map中查找配置,找到就取出configVarBase,
//        然后dynamic_pointer_cast函数强转一个coinfigvar的智能指针返回
        template<class T>
        static typename ConfigVar<T>::ptr  Lookup(const std::string& name){
            RWMutexType::ReadLock lock(GetMutex());
            auto it = GetDatas().find(name);
            if(it==GetDatas().end()){
                return nullptr;
            }
//            然后dynamic_pointer_cast函数强转一个coinfigvar的智能指针返回
            return std::dynamic_pointer_cast<ConfigVar<T> >(it->second);
        }

        static void LoadFormYaml(const YAML::Node& root);

        static ConfigVarBase::ptr LookupBase(const std::string& name);

        static void Visit(std::function<void(ConfigVarBase::ptr)> cb);
    private:
        static ConfigVarMap& GetDatas() {
            static ConfigVarMap s_data;
            return s_data;
        }
//        static ConfigVarMap s_data;

        static RWMutexType& GetMutex(){
            static RWMutexType s_mutex;
            return s_mutex;
        }
    };
}
#endif //SERVER_FRAMEWORK_CONFIG_H
